
package loader.sandbox;

import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.StringTokenizer;

/**
 * Configure normal URL handling.
 * 
 * 
 * <h3>Security</h3>
 * 
 * An instance of this class is installed by the {@link WebLoader} as
 * the {@link java.net.URL} stream handler factory.
 * 
 * Only one url handler exists over the life of the JVM process.
 * Therefore registered classes must originate from the bootstrap
 * classloader.
 * 
 * 
 * <h3>Access</h3>
 * 
 * This url stream handler never returns a stream handler for the
 * 'file' protocol to a class loaded by the web loader.  
 * 
 * The application employs the integrated persistence model within the
 * JNLP application.
 * 
 * 
 * <h3>Extension</h3>
 * 
 * Unprotected protocols may be added by calling the register function.
 * 
 * The set of protected protocols are listed in the Proto enumeration.
 * 
 * 
 * <h3>Shared Operation</h3>
 * 
 * The url stream handler factory is shared across all WebLoaders.
 * 
 * The java.net.URL permits the jvm process (bootstrap classloader
 * scope) to register one and only one url stream factory for the life
 * of the jvm process.
 * 
 * Registered classes must be uniform across all WebLoader instances.
 * 
 * Therefore, url stream handler classes must originte in the
 * bootstrap classpath (loader archive).
 * 
 * Web loader application classes will not register.
 * 
 * 
 * @see AbstractContent
 * @author jdp
 */
public final class UrlHandler
    implements URLStreamHandlerFactory
{
    /**
     * Call this once and remember the returned status for value ("if
     * installed" / "available").
     * 
     * @return Change performed
     */
    public final static boolean Register(String proto, Class<URLStreamHandler> clas){
        return Instance.register(proto,clas);
    }
    /**
     * Set of protected protocols.
     */
    public static enum Proto {

        http, https, ftp, file, jar;



        public final static boolean Exclude(Proto proto){
            return (null == proto || 
                    (Proto.file == proto && WebLoader.CallerIsIn()));
        }
        public final static boolean Include(Proto proto){
            return (null != proto && 
                    (Proto.file != proto || WebLoader.CallerIsNotIn()));
        }
        public final static Proto For(String string){
            try {
                return valueOf(string);
            }
            catch (RuntimeException notfound){
                return null;
            }
        }
    }



    private final java.util.Map<String,Class<URLStreamHandler>> registry = 
        new java.util.HashMap<String,Class<URLStreamHandler>>();


    private UrlHandler(){
        super();
        Class cl = loader.sandbox.net.resource.Handler.class;
        Class<URLStreamHandler> clas = (Class<URLStreamHandler>)cl;
        this.registry.put("resource",clas);
    }


    public final boolean register(String proto, Class<URLStreamHandler> clas){

        if (null != proto && null != clas && 0 != proto.length() && null == Proto.For(proto)){

            if (null == this.registry.get(proto)){

                if (clas.getClassLoader() instanceof loader.Main)
                    return false;
                else {
                    this.registry.put(proto,clas);
                    return true;
                }
            }
        }
        return false;
    }
    public URLStreamHandler createURLStreamHandler(Proto proto){
        if (Proto.Include(proto)){
            String name = proto.name();
            Class<URLStreamHandler> clas = this.registry.get(name);
            if (null != clas){
                try {
                    return clas.newInstance();
                }
                catch (Exception exc){
                    if (WebLoader.Test){
                        System.err.println(String.format("Warning: error for protocol '%s' in class '%s'; kind '%s'; msg '%s'.",name,clas.getName(),exc.getClass().getName(),exc.getMessage()));
                        if (WebLoader.Debug)
                            exc.printStackTrace();
                    }
                }
            }
            return SunHandler(name);
        }
        else 
            return null;
    }
    public URLStreamHandler createURLStreamHandler(String protocol){
        Proto proto = Proto.For(protocol);
        return this.createURLStreamHandler(proto);
    }

    /** 
     * System Properties
     */
    final static class Prop {

        final static String ProtocolPathName = "java.protocol.handler.pkgs";

        final static String ProtocolPathDefault = "loader.sandbox.net";
    }
    private final static String[] SunPath;
    static {
        String path = System.getProperty(Prop.ProtocolPathName);
        if (null == path) path = "";
        StringTokenizer strtok = new StringTokenizer(path,"|");
        int count = strtok.countTokens();
        String[] list = new String[count+1];
        for (int cc = 0; cc < count; cc++)
            list[cc] = strtok.nextToken();
        list[count] = "sun.net.www.protocol";
        SunPath = list;
    }
    private final static int SunPathCount = SunPath.length;

    private final URLStreamHandler SunHandler(String path){
        for (int cc = 0; cc < SunPathCount; cc++){
            String pkg = SunPath[cc];
            String classname = WebLoader.Pcat(pkg,path,".Handler");
            try {
                Class<URLStreamHandler> clas = (Class<URLStreamHandler>)Class.forName(classname);
                return clas.newInstance();
            }
            catch (Exception exc){
            }
        }
        return null;
    }

    private final static UrlHandler Instance = new UrlHandler();

    static void Create(){
        try {
            System.setProperty(Prop.ProtocolPathName,Prop.ProtocolPathDefault);
            /*
             * Set once
             */
            URL.setURLStreamHandlerFactory(Instance);
        }
        catch (Exception exc){
        }
    }
}
